<!DOCTYPE html>
<html lang="en">
  <head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
 
<link rel="stylesheet" href="../assets/css/style.css"/> 
<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->    <title> GoJS Ports in Nodes-- Northwoods Software </title>
    <link rel="stylesheet" href="../assets/css/prism.css"  />
  </head>
  <script>
    
    window.diagrams = [];
    window.goCode = function(pre, w, h, parentid, animation) {
      window.diagrams.push([pre, w, h, parentid, animation]);
    }
  </script>
  <body>
  <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
    <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
      <div class="md:pl-4">
        <a class="text-white hover:text-white no-underline hover:no-underline
        font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
          <h1 class="mb-0 p-1 ">GoJS</h1>
        </a>
      </div>
      <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
        <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
          <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
          <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
        </svg>
      </button>
      <div id="topnavList" class="hidden lg:text-base sm:block items-center w-auto mt-0 text-white p-0 z-20">
        <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
        </ul>
      </div>
    </div>
    <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
  </nav>
    
    <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
      
    <div id="navSide" class="flex flex-col w-full md:w-40 lg:w-48 text-gray-700 bg-white flex-shrink-0">
      <div class="flex-shrink-0 px-8 py-4">
        <button id="navButton" class="rounded-lg md:hidden focus:outline-none focus:ring" aria-label="Navigation">
          <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
            <path id="navOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
            <path id="navClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
          </svg>
        </button>
      </div>
      <nav id="navList" class="min-h-screen hidden md:block sidebar-nav flex-grow px-1 lg:px-4 pb-4 md:pb-0 md:overflow-y-auto break-words">
  <a href="index.html">Basics</a>
  <a href="buildingObjects.html">Building Parts</a>
  <a href="usingModels.html">Using Models</a>
  <a href="dataBinding.html">Data Binding</a>
  <a href="react.html">GoJS with React</a>
  <a href="angular.html">GoJS with Angular</a>
  <a href="textBlocks.html">TextBlocks</a>
  <a href="shapes.html">Shapes</a>
  <a href="pictures.html">Pictures</a>
  <a href="panels.html">Panels</a>
  <a href="tablePanels.html">Table Panels</a>
  <a href="brush.html">Brushes</a>
  <a href="sizing.html">Sizing Objects</a>
  <a href="itemArrays.html">Item Arrays</a>
  <a href="changedEvents.html">Changed Events</a>
  <a href="transactions.html">Transactions</a>
  <a href="viewport.html">Coordinates</a>
  <a href="initialView.html">Initial View</a>
  <a href="collections.html">Collections</a>
  <a href="links.html">Links</a>
  <a href="linkLabels.html">Link Labels</a>
  <a href="connectionPoints.html">Link Points</a>
  <a href="ports.html">Ports</a>
  <a href="nodes.html">Nodes</a>
  <a href="debugging.html">Debugging</a>
  <a href="layouts.html">Layouts</a>
  <a href="trees.html">Trees</a>
  <a href="subtrees.html">SubTrees</a>
  <a href="groups.html">Groups</a>
  <a href="subgraphs.html">SubGraphs</a>
  <a href="sizedGroups.html">Sized Groups</a>
  <a href="selection.html">Selection</a>
  <a href="highlighting.html">Highlighting</a>
  <a href="animation.html">Animation</a>
  <a href="toolTips.html">ToolTips</a>
  <a href="contextmenus.html">Context Menus</a>
  <a href="events.html">Diagram Events</a>
  <a href="tools.html">Tools</a>
  <a href="commands.html">Commands</a>
  <a href="permissions.html">Permissions</a>
  <a href="validation.html">Validation</a>
  <a href="HTMLInteraction.html">HTML Interaction</a>
  <a href="layers.html">Layers &amp; Z-ordering</a>
  <a href="palette.html">Palette</a>
  <a href="overview.html">Overview</a>
  <a href="resizing.html">Resizing Diagrams</a>
  <a href="replacingDeleting.html">Replacing and Deleting</a>
  <a href="buttons.html">Buttons</a>
  <a href="templateMaps.html">Template Maps</a>
  <a href="legends.html">Legends and Titles</a>
  <a href="extensions.html">Extensions</a>
  <a href="geometry.html">Geometry Strings</a>
  <a href="grids.html">Grid Patterns</a>
  <a href="graduatedPanels.html">Graduated Panels</a>
  <a href="makingImages.html">Diagram Images</a>
  <a href="makingSVG.html">Diagram SVG</a>
  <a href="printing.html">Printing</a>
  <a href="serverSideImages.html">Server-side Images</a>
  <a href="nodeScript.html">GoJS in Node.js</a>
  <a href="testing.html">Testing</a>
  <a href="storage.html">Storage</a>
  <a href="performance.html">Performance</a>
  <a href="source.html">Building from Source</a>
  <a href="platforms.html">Platforms</a>
  <a href="deployment.html">Deployment</a>
      </nav>
    </div>      
      <div class="pt-4 px-2 md:px-0 lg:px-4 pb-16 w-full overflow-hidden">




<h1>Ports in Nodes</h1>
<p>
Although you have some control over where links will connect at a node (at a particular spot, along one or
more sides, or at the intersection with the edge), there are times when you want to have different logical
and graphical places at which links should connect.  The elements at which a link may connect are called
<i>ports</i>.  There may be any number of ports in a node.  By default there is just one port, the whole node,
which results in the effect of having the whole node act as the port, as you have seen in all of the previous examples.
</p>
<p>
To declare that a particular element in a <a>Node</a> is a port, set the <a>GraphObject.portId</a> property to a string.
Note that the port-or-link-related properties may apply to any element in the visual tree of the node,
which is why they are properties of <a>GraphObject</a> rather than <a>Node</a>.
</p>
<p>
Port-like GraphObjects can only be in <a>Node</a>s or <a>Group</a>s, not in <a>Link</a>s or <a>Adornment</a>s or simple <a>Part</a>s.
So there is no reason to try to set <a>GraphObject.portId</a> on any object in a Link.
</p>
<p>
<p>
See samples that make use of ports in the <a href="../samples/index.html#ports">samples index</a>.
</p>
</p>

<h2 id="SinglePorts">Single Ports</h2>
<p>
In many situations you want to consider links logically related to the node as a whole but you don't want links
connecting to the whole node.
In this case each node has only one port, but you do not want the whole node to act as the one port.
</p>
<p>
For example, consider how links connect to the nodes when the whole node is acting as a port in one common manner.
The <a>GraphObject.fromSpot</a> and <a>GraphObject.toSpot</a> are at the middles of the sides.
Because the height of the whole node includes the text label,
the middle of the side is not the middle of the "icon", which in this case is a circle.
</p>
<pre class="lang-js" id="defaultPort"><code>
  diagram.nodeTemplate =
    $(go.Node, "Vertical",
      { fromSpot: go.Spot.Right, toSpot: go.Spot.Left },   // port properties on the node
      $(go.Shape, "Ellipse",
        { width: 30, height: 30, fill: "green" }),
      $(go.TextBlock,
        { font: "20px sans-serif" },
        new go.Binding("text", "key"))
    );

  var nodeDataArray = [
    { key: "Alpha" },
    { key: "Beta" }
  ];
  var linkDataArray = [
    { from: "Alpha", to: "Beta" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>goCode("defaultPort", 600, 100)</script>
<p>
This appearance does not look or behave quite right.
Really we want links to connect to the circular <a>Shape</a>.
</p>

<p>
If you want a particular element to act as the port rather than the whole node,
just set its <a>GraphObject.portId</a> to the empty string.
The empty string is the name of the default port.
</p>
<p>
In this example, we set <a>GraphObject.portId</a> on the circular shape.
Note that we move the other port-related properties, such as the port spots, to that object too.
</p>
<pre class="lang-js" id="singlePort"><code>
  diagram.nodeTemplate =
    $(go.Node, "Vertical",
      $(go.Shape, "Ellipse",
        { width: 30, height: 30, fill: "green",
          portId: "",  // now the Shape is the port, not the whole Node
          fromSpot: go.Spot.Right,  // port properties go on the port!
          toSpot: go.Spot.Left
        }),
      $(go.TextBlock,
        { font: "20px sans-serif" },
        new go.Binding("text", "key"))
    );

  var nodeDataArray = [
    { key: "Alpha" },
    { key: "Beta" }
  ];
  var linkDataArray = [
    { from: "Alpha", to: "Beta" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>goCode("singlePort", 600, 100)</script>
<p>
Notice how the links nicely connect the circular shapes by ignoring the text labels.
</p>

<h2 id="GeneralPorts">General Ports</h2>
<p>
It is also common to have diagrams where you want more than one port in a node.
The number of ports might even vary dynamically.
</p>
<p>
In order for a link data object to distinguish which port the link should connect to,
the <a>GraphLinksModel</a> supports two additional data properties that identify the names of the ports in the nodes
at both ends of the link.
<a>GraphLinksModel.getToKeyForLinkData</a> identifies the node to connect to;
<a>GraphLinksModel.getToPortIdForLinkData</a> identifies the port within the node.
Similarly, <a>GraphLinksModel.getFromKeyForLinkData</a> and <a>GraphLinksModel.getFromPortIdForLinkData</a> identify the node and its port.
</p>
<p>
Normally a <a>GraphLinksModel</a> assumes that there is no need to recognize port information on link data.
If you want to support port identifiers on link data, you need to set <a>GraphLinksModel.linkToPortIdProperty</a>
and <a>GraphLinksModel.linkFromPortIdProperty</a> to be the names of the link data properties.
If you do not set these properties, all port identifiers are assumed to be the empty string,
which is the name of the one default port for a node.
</p>
<p class="box bg-danger">
If you have set or bound <a>GraphObject.portId</a> on any element to be a non-empty string,
you will need to use a <a>GraphLinksModel</a> and set <a>GraphLinksModel.linkToPortIdProperty</a>
and <a>GraphLinksModel.linkFromPortIdProperty</a> to be the names of two properties on your link data,
or you will need to hard code the portId names in the link template(s)
(i.e. <a>Link.fromPortId</a> and <a>Link.toPortId</a>),
in order for the user to be able to link with those ports.
</p>
<pre class="lang-js" id="ports"><code>
  diagram.nodeTemplate =
    $(go.Node, "Auto",
      $(go.Shape, "Rectangle", { fill: "lightgray" }),
      $(go.Panel, "Table",
        $(go.RowColumnDefinition,
          { column: 0, alignment: go.Spot.Left}),
        $(go.RowColumnDefinition,
          { column: 2, alignment: go.Spot.Right }),
        $(go.TextBlock,  // the node title
          { column: 0, row: 0, columnSpan: 3, alignment: go.Spot.Center,
            font: "bold 10pt sans-serif", margin: new go.Margin(4, 2) },
          new go.Binding("text", "key")),
        $(go.Panel, "Horizontal",
          { column: 0, row: 1 },
          $(go.Shape,  // the "A" port
            { width: 6, height: 6, portId: "A", toSpot: go.Spot.Left }),
          $(go.TextBlock, "A")  // "A" port label
        ),
        $(go.Panel, "Horizontal",
          { column: 0, row: 2 },
          $(go.Shape,  // the "B" port
            { width: 6, height: 6, portId: "B", toSpot: go.Spot.Left }),
          $(go.TextBlock, "B")  // "B" port label
        ),
        $(go.Panel, "Horizontal",
          { column: 2, row: 1, rowSpan: 2 },
          $(go.TextBlock, "Out"),  // "Out" port label
          $(go.Shape,  // the "Out" port
            { width: 6, height: 6, portId: "Out", fromSpot: go.Spot.Right })
        )
      )
    );

  diagram.linkTemplate =
    $(go.Link,
      { routing: go.Link.Orthogonal, corner: 3 },
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" })
    );

  diagram.layout = $(go.LayeredDigraphLayout, { columnSpacing: 10 });

  diagram.model =
    $(go.GraphLinksModel,
      { linkFromPortIdProperty: "fromPort",  // required information:
        linkToPortIdProperty: "toPort",      // identifies data property names
        nodeDataArray: [
          { key: "Add1" },
          { key: "Add2" },
          { key: "Subtract1" }
        ],
        linkDataArray: [
          { from: "Add1", fromPort: "Out", to: "Subtract1", toPort: "A" },
          { from: "Add2", fromPort: "Out", to: "Subtract1", toPort: "B" }
        ] });
</code></pre>
<script>goCode("ports", 600, 150)</script>

<h2 id="DrawingNewLinks">Drawing new Links</h2>
<p>
Setting either or both of the <a>GraphObject.fromLinkable</a> and <a>GraphObject.toLinkable</a>
properties to true allows users to interactively draw new links between ports.
</p>
<p>
To draw a new link, mouse down on an "Out" port, move (drag) to nearby an input port,
and then mouse-up to complete the link.
</p>
<pre class="lang-js" id="linkablePorts"><code>
  diagram.nodeTemplate =
    $(go.Node, "Auto",
      $(go.Shape, "Rectangle", { fill: "lightgray" }),
      $(go.Panel, "Table",
        $(go.RowColumnDefinition,
          { column: 0, alignment: go.Spot.Left}),
        $(go.RowColumnDefinition,
          { column: 2, alignment: go.Spot.Right }),
        $(go.TextBlock,  // the node title
          { column: 0, row: 0, columnSpan: 3, alignment: go.Spot.Center,
            font: "bold 10pt sans-serif", margin: new go.Margin(4, 2) },
          new go.Binding("text", "key")),
        $(go.Panel, "Horizontal",
          { column: 0, row: 1 },
          $(go.Shape,  // the "A" port
            { width: 6, height: 6, portId: "A", toSpot: go.Spot.Left,
              toLinkable: true, toMaxLinks: 1 }),  // allow user-drawn links from here
          $(go.TextBlock, "A")  // "A" port label
        ),
        $(go.Panel, "Horizontal",
          { column: 0, row: 2 },
          $(go.Shape,  // the "B" port
            { width: 6, height: 6, portId: "B", toSpot: go.Spot.Left,
              toLinkable: true, toMaxLinks: 1 }),  // allow user-drawn links from here
          $(go.TextBlock, "B")  // "B" port label
        ),
        $(go.Panel, "Horizontal",
          { column: 2, row: 1, rowSpan: 2 },
          $(go.TextBlock, "Out"),  // "Out" port label
          $(go.Shape,  // the "Out" port
            { width: 6, height: 6, portId: "Out", fromSpot: go.Spot.Right,
              fromLinkable: true })  // allow user-drawn links to here
        )
      )
    );

  diagram.linkTemplate =
    $(go.Link,
      { routing: go.Link.Orthogonal, corner: 3 },
      $(go.Shape),
      $(go.Shape, { toArrow: "Standard" })
    );

  diagram.layout = $(go.LayeredDigraphLayout, { columnSpacing: 10 });

  diagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;

  diagram.model =
    $(go.GraphLinksModel,
      { linkFromPortIdProperty: "fromPort",  // required information:
        linkToPortIdProperty: "toPort",      // identifies data property names
        nodeDataArray: [
          { key: "Add1" },
          { key: "Add2" },
          { key: "Subtract1" }
        ],
        linkDataArray: [
          // no predeclared links
        ] });
</code></pre>
<script>goCode("linkablePorts", 600, 250)</script>
<p>
By default the user may not draw more than one link in the same direction between any pair of ports,
nor may the user draw a link connecting a node with itself.
Please read a general discussion of <a href="validation.html">Linking Validation</a>.
</p>
<p>
By setting <a>GraphObject.toMaxLinks</a> to 1, as shown in this example, the user may draw at most one link going into that port.
And because <a>GraphObject.fromLinkable</a> is false for that port element, the user will not be able to connect any links coming out of that port.
</p>
<p>
  If you want to prevent the user from connecting any more than one Link with a Node, regardless of direction,
  you will need to implement a <a>LinkingBaseTool.linkValidation</a> or a <a>Node.linkValidation</a> predicate.
  See the discussion about <a href="validation.html#GeneralLinkingValidation">General Linking Validation</a>
</p>

      </div>
    </div>
  
  <div class="bg-nwoods-primary">
    <section class="max-w-screen-lg text-white container mx-auto py-2 px-12">
      <p id="version" class="leading-none mb-2 my-4">GoJS</p>
    </section>
  </div><footer class="bg-nwoods-primary text-white">
  <div class="container max-w-screen-lg mx-auto  px-8">
    <div class="w-full py-6">

        <div class="max-w-screen-lg xl:max-w-screen-xl mx-auto px-4 sm:px-6 md:px-8">
          <ul class="text-sm font-medium pb-14 sm:pb-20 grid grid-cols-1 sm:grid-cols-3 gap-y-10">
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">GoJS</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="../samples/index.html">Samples</a>
                </li>
                <li>
                  <a href="../learn/index.html">Learn</a>
                </li>
                <li>
                  <a href="../intro/index.html">Intro</a>
                </li>
                <li>
                  <a href="../api/index.html">API</a>
                </li>
                <li>
                  <a href="../changelog.html">Changelog</a>
                </li>
                <li>
                  <a href="https://github.com/NorthwoodsSoftware/GoJS">GitHub</a>
                </li>
              </ul>
            </li>
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">Support</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="https://www.nwoods.com/contact.html"
                  target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a>
                </li>
                <li>
                  <a href="https://forum.nwoods.com/c/gojs">Forum</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/app/activate.aspx?sku=gojs">Activate</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/sales/index.html"
                  target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a>
                </li>
                <li>
                  <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">Videos</a>
                </li>
              </ul>
            </li>
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">Company</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="https://www.nwoods.com">Northwoods</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/about.html">About Us</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/contact.html">Contact Us</a>
                </li>
                <li>
                  <a href="https://twitter.com/northwoodsgo">Twitter</a>
                </li>

              </ul>
            </li>
          </ul>


      <p class="text-sm text-gray-100 md:mb-6">
        Copyright 1998-2021 <a class="text-white" href="https://www.nwoods.com">Northwoods Software</a>
      </p>
    </div>
  </div>
</footer>  </body>

<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1506307-5"></script> 
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date()); gtag('config', 'UA-1506307-5');
  var getOutboundLink = function(url, label) {
    gtag('event', 'click', {
      'event_category': 'outbound',
      'event_label': label,
      'transport_type': 'beacon'
    });
  }

  // topnav
  var topButton = document.getElementById("topnavButton");
  var topnavList = document.getElementById("topnavList");
  topButton.addEventListener("click", function() {
    this.classList.toggle("active");
    topnavList.classList.toggle("hidden");
    document.getElementById("topnavOpen").classList.toggle("hidden");
    document.getElementById("topnavClosed").classList.toggle("hidden");
  });
</script>
  <script src="../assets/js/prism.js"></script>
  <script src="../release/go.js"></script>
  <script src="../assets/js/goDoc.js"></script>
  <script>
    document.addEventListener("DOMContentLoaded", function() {
      if (window.go) document.getElementById('version').textContent = "GoJS version " + go.version;
      if (window.goDoc) window.goDoc();
      var d = window.diagrams;
      for (var i = 0; i < d.length; i++) {
        var dargs = d[i];
        goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
      }
      if (window.extra) window.extra();
    });
  </script>
</html>
